-- ===== SavedVariables =====
PixelCrosshairDB = PixelCrosshairDB or {
    colorEnabled = true,
    effectsEnabled = true,
    visible = true,
    colors = {
        enemy = {1, 0.341, 0.451}, -- #FF5773
        neutral = {1, 0.737, 0.341}, -- #FFBC57
        friendly = {0.5, 1, 0.7}, -- #80FFB3 (seafoam)
        healing = {0.639, 1, 0.4}, -- #A3FF66
    },
    opacity = 0.5,
    posX = 0,
    posY = 6,
    lastHex = {},
    crosshairSize = 7, -- Crosshair size variable
    crosshairThickness = 1, -- Crosshair thickness variable
}

-- ===== Helpers =====
local function RGBtoHex(r, g, b)
    return string.format("#%02X%02X%02X", math.floor(r * 255), math.floor(g * 255), math.floor(b * 255))
end

local function HexToRGB(hex, fallbackR, fallbackG, fallbackB)
    if not hex then return fallbackR, fallbackG, fallbackB end
    hex = hex:gsub("#", "")
    if #hex ~= 6 then return fallbackR, fallbackG, fallbackB end
    local r = tonumber("0x" .. hex:sub(1, 2))
    local g = tonumber("0x" .. hex:sub(3, 4))
    local b = tonumber("0x" .. hex:sub(5, 6))
    if not r or not g or not b then return fallbackR, fallbackG, fallbackB end
    return r / 255, g / 255, b / 255
end

-- Load saved colors
PixelCrosshairDB.lastHex = PixelCrosshairDB.lastHex or {}
for key, hex in pairs(PixelCrosshairDB.lastHex) do
    local r, g, b = HexToRGB(hex, unpack(PixelCrosshairDB.colors[key] or {1, 1, 1}))
    PixelCrosshairDB.colors[key] = {r, g, b}
end

-- Ensure lastHex has all keys
for key, color in pairs(PixelCrosshairDB.colors) do
    if not PixelCrosshairDB.lastHex[key] then
        PixelCrosshairDB.lastHex[key] = RGBtoHex(unpack(color))
    end
end

-- ===== Frame =====
local f = CreateFrame("Frame", "PixelCrosshair", UIParent)
f:SetSize(PixelCrosshairDB.crosshairSize, PixelCrosshairDB.crosshairSize)
f:SetPoint("CENTER", UIParent, "CENTER", PixelCrosshairDB.posX, PixelCrosshairDB.posY)
f:SetFrameStrata("TOOLTIP")
f:SetFrameLevel(100)
if not PixelCrosshairDB.visible then f:Hide() end

-- Crosshair Logic
local length = PixelCrosshairDB.crosshairSize
local thickness = PixelCrosshairDB.crosshairThickness
local offsetY = 2
local colorEnabled = PixelCrosshairDB.colorEnabled
local effectsEnabled = PixelCrosshairDB.effectsEnabled
local opacity = PixelCrosshairDB.opacity

local vLeft, vRight = {}, {}
local plusTop, plusBottom, plusLeft, plusRight = {}, {}, {}, {}

-- Build Crosshair
local function BuildCrosshairLines()
    -- Clear old lines
    for _, line in ipairs(vLeft) do line:Hide() end
    for _, line in ipairs(vRight) do line:Hide() end
    for _, line in ipairs(plusLeft) do line:Hide() end
    for _, line in ipairs(plusRight) do line:Hide() end
    for _, line in ipairs(plusTop) do line:Hide() end
    for _, line in ipairs(plusBottom) do line:Hide() end

    -- Build the crosshair
    vLeft, vRight = {}, {}
    plusTop, plusBottom, plusLeft, plusRight = {}, {}, {}, {}

    -- Build V (idle crosshair)
    for i = 0, length - 1 do
        local l = f:CreateTexture(nil, "OVERLAY")
        l:SetSize(thickness, 1)
        l:SetPoint("CENTER", f, "CENTER", -(i), -i + offsetY)
        l:SetTexture("Interface\\Buttons\\WHITE8X8")
        l:SetVertexColor(1, 1, 1, opacity)
        table.insert(vLeft, l)

        local r = f:CreateTexture(nil, "OVERLAY")
        r:SetSize(thickness, 1)
        r:SetPoint("CENTER", f, "CENTER", i, -i + offsetY)
        r:SetTexture("Interface\\Buttons\\WHITE8X8")
        r:SetVertexColor(1, 1, 1, opacity)
        table.insert(vRight, r)
    end

    -- Build target dashes
    for i = 0, length - 1 do
        for _, side in ipairs({"Left", "Right", "Top", "Bottom"}) do
            local t = f:CreateTexture(nil, "OVERLAY")
            t:SetSize(thickness, 1)
            local x, y = 0, 0
            if side == "Left" then x = -(2 + 1 + i) elseif side == "Right" then x = 2 + 1 + i
            elseif side == "Top" then y = 2 + 1 + i elseif side == "Bottom" then y = -(2 + 1 + i) end
            t:SetPoint("CENTER", f, "CENTER", x, y)
            t:SetTexture("Interface\\Buttons\\WHITE8X8")
            t:SetVertexColor(1, 1, 1, opacity)
            t:Hide()
            if side == "Left" then table.insert(plusLeft, t)
            elseif side == "Right" then table.insert(plusRight, t)
            elseif side == "Top" then table.insert(plusTop, t)
            elseif side == "Bottom" then table.insert(plusBottom, t) end
        end
    end
end
BuildCrosshairLines()

-- ===== Slash Commands =====
SLASH_PIXCROSS1 = "/dot"
SlashCmdList["PIXCROSS"] = function()
    if f:IsShown() then
        f:Hide()
        PixelCrosshairDB.visible = false
    else
        f:Show()
        PixelCrosshairDB.visible = true
    end
end

SLASH_PIXCROSSCONFIG1 = "/dotconfig"
SlashCmdList["PIXCROSSCONFIG"] = function()
    CreateConfigFrame()
end

-- ===== Config Frame =====
function CreateConfigFrame()
    if PixelCrosshairConfigFrame then
        PixelCrosshairConfigFrame:SetShown(not PixelCrosshairConfigFrame:IsShown())
        return
    end

    local cf = CreateFrame("Frame", "PixelCrosshairConfigFrame", UIParent, "BasicFrameTemplateWithInset")
    cf:SetSize(340, 480)
    cf:SetPoint("CENTER")
    cf:SetFrameStrata("DIALOG")
    cf:SetMovable(true)
    cf:EnableMouse(true)
    cf:RegisterForDrag("LeftButton")
    cf:SetScript("OnDragStart", cf.StartMoving)
    cf:SetScript("OnDragStop", cf.StopMovingOrSizing)
    cf.title = cf:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    cf.title:SetPoint("TOP", cf.TitleBg, "TOP", 0, -4)
    cf.title:SetText("PixelCrosshair Config")

    -- Checkbox helper
    local function createCheckBox(name, label, posY, initial, onClick)
        local cb = CreateFrame("CheckButton", name, cf, "UICheckButtonTemplate")
        cb:SetPoint("TOPLEFT", 10, posY)
        cb.text:SetText(label)
        cb:SetChecked(initial)
        cb:SetScript("OnClick", function(self) onClick(self:GetChecked()) end)
        return cb
    end

    createCheckBox("PC_CBVisible", "Show Crosshair", -30, PixelCrosshairDB.visible, function(val)
        if val then f:Show() else f:Hide() end
        PixelCrosshairDB.visible = val
    end)

    createCheckBox("PC_CBColor", "Enable Colors", -60, colorEnabled, function(val)
        colorEnabled = val
        PixelCrosshairDB.colorEnabled = val
    end)

    createCheckBox("PC_CBEff", "Enable Effects", -90, effectsEnabled, function(val)
        effectsEnabled = val
        PixelCrosshairDB.effectsEnabled = val
    end)

    -- ===== Color pickers =====
    local function createColorPicker(label, posY, colorTable)
        local key = label:lower()
        local cp = CreateFrame("Button", nil, cf, "UIPanelButtonTemplate")
        cp:SetSize(70, 20)
        cp:SetPoint("TOPLEFT", 10, posY)
        cp:SetText(label)

        local swatch = cf:CreateTexture(nil, "OVERLAY")
        swatch:SetSize(20, 20)
        swatch:SetPoint("LEFT", cp, "RIGHT", 5, 0)
        swatch:SetTexture("Interface\\Buttons\\WHITE8X8")
        swatch:SetVertexColor(unpack(colorTable))

        local hexBox = CreateFrame("EditBox", nil, cf, "InputBoxTemplate")
        hexBox:SetSize(60, 20)
        hexBox:SetPoint("LEFT", swatch, "RIGHT", 5, 0)
        hexBox:SetText(PixelCrosshairDB.lastHex[key] or RGBtoHex(unpack(colorTable)))
        hexBox:SetAutoFocus(false)
        hexBox:SetMaxLetters(7)

        hexBox:SetScript("OnEnterPressed", function(self)
            local text = self:GetText()
            local r, g, b = HexToRGB(text, unpack(colorTable))
            colorTable[1], colorTable[2], colorTable[3] = r, g, b
            PixelCrosshairDB.colors[key] = {r, g, b}
            PixelCrosshairDB.lastHex[key] = RGBtoHex(r, g, b)
            swatch:SetVertexColor(r, g, b)
            self:SetText(RGBtoHex(r, g, b))
            self:ClearFocus()
        end)

        cp:SetScript("OnClick", function()
            local prev = {unpack(colorTable)}
            ColorPickerFrame:SetColorRGB(unpack(colorTable))
            ColorPickerFrame.hasOpacity = false
            ColorPickerFrame.previousValues = prev
            ColorPickerFrame.func = function()
                local r, g, b = ColorPickerFrame:GetColorRGB()
                colorTable[1], colorTable[2], colorTable[3] = r, g, b
                PixelCrosshairDB.colors[key] = {r, g, b}
                PixelCrosshairDB.lastHex[key] = RGBtoHex(r, g, b)
                swatch:SetVertexColor(r, g, b)
                hexBox:SetText(RGBtoHex(r, g, b))
            end
            ColorPickerFrame.cancelFunc = function()
                colorTable[1], colorTable[2], colorTable[3] = unpack(prev)
                PixelCrosshairDB.colors[key] = {unpack(prev)}
                PixelCrosshairDB.lastHex[key] = RGBtoHex(unpack(prev))
                swatch:SetVertexColor(unpack(prev))
                hexBox:SetText(RGBtoHex(unpack(prev)))
            end
            ColorPickerFrame:Hide()
            ColorPickerFrame:Show()
        end)
    end

    createColorPicker("Enemy", -130, PixelCrosshairDB.colors.enemy)
    createColorPicker("Neutral", -160, PixelCrosshairDB.colors.neutral)
    createColorPicker("Friendly", -190, PixelCrosshairDB.colors.friendly)
    createColorPicker("Healing", -220, PixelCrosshairDB.colors.healing)

    -- Sliders for Position, Size, Thickness, Opacity
    local function createXYSlider(name, label, posY, initial, minv, maxv, setFunc)
        local s = CreateFrame("Slider", name, cf, "OptionsSliderTemplate")
        s:SetSize(200, 20)
        s:SetPoint("TOPLEFT", 10, posY)
        s:SetMinMaxValues(minv, maxv)
        s:SetValueStep(1)
        s:SetObeyStepOnDrag(true)
        s:SetValue(initial)
        _G[s:GetName() .. "Text"]:SetText(label)

        local tb = CreateFrame("EditBox", nil, cf, "InputBoxTemplate")
        tb:SetSize(40, 20)
        tb:SetPoint("LEFT", s, "RIGHT", 10, 0)
        tb:SetText(initial)
        tb:SetAutoFocus(false)
        tb:SetMaxLetters(4)
        tb:SetScript("OnEnterPressed", function(self)
            local v = tonumber(self:GetText()) or initial
            v = math.min(maxv, math.max(minv, v))
            setFunc(v)
            s:SetValue(v)
            self:ClearFocus()
        end)

        s:SetScript("OnValueChanged", function(self, v)
            setFunc(v)
            tb:SetText(math.floor(v))
        end)
    end

    -- Sliders at the bottom
    createXYSlider("PC_XSlider", "X Position", -300, PixelCrosshairDB.posX, -200, 200, function(v)
        PixelCrosshairDB.posX = v
        f:SetPoint("CENTER", UIParent, "CENTER", v, PixelCrosshairDB.posY)
    end)

    createXYSlider("PC_YSlider", "Y Position", -330, PixelCrosshairDB.posY, -200, 200, function(v)
        PixelCrosshairDB.posY = v
        f:SetPoint("CENTER", UIParent, "CENTER", PixelCrosshairDB.posX, v)
    end)

    createXYSlider("PC_SizeSlider", "Crosshair Size", -360, PixelCrosshairDB.crosshairSize, 3, 20, function(v)
        PixelCrosshairDB.crosshairSize = v
        f:SetSize(v, v)
        length = v
        -- Adjust the target dashes as well
        for i, dash in ipairs(plusLeft) do
            dash:SetSize(v, 1)
        end
        for i, dash in ipairs(plusRight) do
            dash:SetSize(v, 1)
        end
        for i, dash in ipairs(plusTop) do
            dash:SetSize(1, v)
        end
        for i, dash in ipairs(plusBottom) do
            dash:SetSize(1, v)
        end
        BuildCrosshairLines()
    end)

    createXYSlider("PC_ThicknessSlider", "Crosshair Thickness", -390, PixelCrosshairDB.crosshairThickness, 1, 5, function(v)
        PixelCrosshairDB.crosshairThickness = v
        thickness = v
        BuildCrosshairLines()
    end)

    -- Opacity Slider
    local slider = CreateFrame("Slider", "PC_OpacitySlider", cf, "OptionsSliderTemplate")
    slider:SetSize(200, 20)
    slider:SetPoint("TOPLEFT", 10, -420)
    slider:SetMinMaxValues(0.1, 1)
    slider:SetValueStep(0.01)
    slider:SetObeyStepOnDrag(true)
    slider:SetValue(PixelCrosshairDB.opacity)
    _G[slider:GetName() .. "Text"]:SetText("Opacity")

    local valBox = CreateFrame("EditBox", nil, cf, "InputBoxTemplate")
    valBox:SetSize(40, 20)
    valBox:SetPoint("LEFT", slider, "RIGHT", 10, 0)
    valBox:SetText(math.floor(PixelCrosshairDB.opacity * 100))
    valBox:SetAutoFocus(false)
    valBox:SetMaxLetters(3)
    valBox:SetScript("OnEnterPressed", function(self)
        local v = tonumber(self:GetText()) or 100
        v = math.min(100, math.max(0, v))
        PixelCrosshairDB.opacity = v / 100
        slider:SetValue(v / 100)
        self:ClearFocus()
    end)

    slider:SetScript("OnValueChanged", function(self, v)
        PixelCrosshairDB.opacity = v
        valBox:SetText(math.floor(v * 100))
    end)

    PixelCrosshairConfigFrame = cf
    cf:Show()
end

-- ===== Crosshair Logic Update =====
local t = 0
local curX, curY = PixelCrosshairDB.posX, PixelCrosshairDB.posY
local curR, curG, curB = unpack(PixelCrosshairDB.colors.neutral)
local targetR, targetG, targetB = 1, 1, 1
local flareTimer = 0
local flaring = false

local function DetermineTargetColor()
    if not colorEnabled then return 1, 1, 1 end
    if UnitIsUnit("player", "target") then return unpack(PixelCrosshairDB.colors.healing) end
    if UnitExists("target") and not UnitIsDeadOrGhost("target") then
        if UnitIsPlayer("target") then
            if UnitIsFriend("player", "target") then
                return unpack(PixelCrosshairDB.colors.friendly) -- Seafoam for friendly NPCs
            else
                return unpack(PixelCrosshairDB.colors.enemy)
            end
        else
            local classification = UnitClassification("target") or "normal"
            local creatureType = UnitCreatureType("target") or ""
            local canAttack = UnitCanAttack("player", "target")
            local reaction = UnitReaction("target", "player") or 5
            local isCritter = classification == "trivial" or creatureType == "Critter"
	    if UnitIsFriend("player", "target") then
    		return unpack(PixelCrosshairDB.colors.friendly)
	    end
            if isCritter or reaction >= 4 then
                return unpack(PixelCrosshairDB.colors.neutral)
            end
            if canAttack and reaction <= 3 then
                return unpack(PixelCrosshairDB.colors.enemy)
            end
            return unpack(PixelCrosshairDB.colors.neutral)
        end
    end
    return 1, 1, 1
end

f:SetScript("OnUpdate", function(self, elapsed)
    t = t + elapsed
    local targeting = UnitExists("target") and not UnitIsDeadOrGhost("target")
    local targetX, targetY = PixelCrosshairDB.posX, PixelCrosshairDB.posY

    if effectsEnabled then
        if targeting then
            targetY = targetY + math.sin(t * 5) * 0.5
        else
            targetY = targetY + math.sin(t * 2) * 1.5
        end
    end

    local lerp = effectsEnabled and (targeting and 0.25 or 0.15) or 1
    curX = curX + (targetX - curX) * lerp
    curY = curY + (targetY - curY) * lerp
    f:SetPoint("CENTER", UIParent, "CENTER", curX, curY)

    for _, l in ipairs(vLeft) do l:SetShown(not targeting) end
    for _, r in ipairs(vRight) do r:SetShown(not targeting) end
    for _, t in ipairs(plusLeft) do t:SetShown(targeting) end
    for _, t in ipairs(plusRight) do t:SetShown(targeting) end
    for _, t in ipairs(plusTop) do t:SetShown(targeting) end
    for _, t in ipairs(plusBottom) do t:SetShown(targeting) end

    targetR, targetG, targetB = DetermineTargetColor()
    curR = curR + (targetR - curR) * 6 * elapsed
    curG = curG + (targetG - curG) * 6 * elapsed
    curB = curB + (targetB - curB) * 6 * elapsed

    for i, l in ipairs(vLeft) do
        l:SetVertexColor(curR, curG, curB, PixelCrosshairDB.opacity)
    end
    for i, r in ipairs(vRight) do
        r:SetVertexColor(curR, curG, curB, PixelCrosshairDB.opacity)
    end

    local dashScale = 1
    if effectsEnabled and targeting then
        dashScale = 1 + 0.2 * math.sin(t * 3)
    end

    if flaring then
        flareTimer = flareTimer + elapsed
        local flareProgress = flareTimer / 0.2
        local flareAmount = 6 * (1 - flareProgress)
        dashScale = dashScale + flareAmount
        if flareTimer >= 0.2 then
            flaring = false
            flareTimer = 0
        end
    end

    -- Adjust the dash sizes for targeting mode based on crosshair size
    for _, tbl in ipairs({plusLeft, plusRight, plusTop, plusBottom}) do
        for i, t in ipairs(tbl) do
            t:SetVertexColor(curR, curG, curB, PixelCrosshairDB.opacity)
            t:SetSize(dashScale, dashScale)
        end
    end
end)

-- ===== Flare trigger =====
local fEvent = CreateFrame("Frame")
fEvent:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
fEvent:SetScript("OnEvent", function(self)
    local timestamp, subevent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, spellID, spellName, spellSchool, amount = CombatLogGetCurrentEventInfo()
    if not UnitExists("target") then return end
    if (subevent == "SPELL_DAMAGE" or subevent == "SPELL_HEAL" or subevent == "SWING_DAMAGE") and destGUID == UnitGUID("target") then
        flaring = true
        flareTimer = 0
    end
end)
